package nio.channel;

import org.junit.Test;

import java.io.*;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.time.Duration;
import java.time.Instant;
import java.util.Map;
import java.util.SortedMap;

/**
 * @program JavaBase
 * @description: 通道
 * @author: zhanglu
 * @create: 2019-06-15 07:24:00
 *
 * 一、通道：用于源节点与目标节点的连接。在java NIO中负责缓冲区中的数据传输
 * Channel本身不存储数据，因此需要配个缓冲区进行传输
 *
 * 二、通道的主要实现类
 *  - java.nio.channels.Channel接口：
 *      - FileChannel
 *      - ServerSocketChannel
 *      - DatagramChannel
 *
 * 三、获取通道
 * 1、getChannel()
 *  本地IO:
 *      - FileInputStream/FileOutputStream
 *      - RandomAccessFile
 *
 *  网络IO
 *      - Socket
 *      - ServerSocket
 *      - DatagramSocket
 *
 * 2、jdk1.7中，nio.2
 *  - 针对各个通道提供了静态方法open()
 *  - Files 工具类 newByteChannel()
 *
 * 四、通道传输
 * transferForm()、transferTo()
 *
 * 五、分散(Scatter) 与 聚集(Gather)
 *  - 分散读取：将通道汇总的数据分散到多个缓冲区中
 *  - 聚集写入：将多个缓冲区中的数据聚集到通道中
 *
 * 六、字符集：Charset
 *  编码：字符串 -> 字节数组
 *  解码：字节数组 -> 字符串
 *  
 *
 *
 *
 */
public class TestChannel01 {

    @Test
    public void test01() throws Exception{ //679000000   744000000

        //时间间隔
        Instant instant1 = Instant.now();

        FileInputStream fin = new FileInputStream(new File("/Users/zhanglu/Downloads/VLFWHPsx.mp4"));
        FileOutputStream fout = new FileOutputStream(new File("/Users/zhanglu/Downloads/test.mp4"));
        FileChannel f1 = fin.getChannel();
        FileChannel f2 = fout.getChannel();
        ByteBuffer buf = ByteBuffer.allocate(1024);
        while(f1.read(buf) != -1){
            buf.flip();
            f2.write(buf);
            buf.clear();
        }
        f2.close();
        f1.close();
        fout.close();
        fin.close();

        Instant instant2 = Instant.now();

        Duration duration = Duration.between(instant1,instant2);

        System.out.println(duration.getNano());
    }

    /**
     * 使用直接缓冲区完成文件的复制(内存映射文件)
     * @throws Exception
     */
    @Test
    public void test02() throws Exception{  //144000000 220000000

        Instant t1 = Instant.now();

        FileChannel f1 = FileChannel.open(Paths.get("/Users/zhanglu/Downloads/VLFWHPsx.mp4"), StandardOpenOption.READ);
        FileChannel f2 = FileChannel.open(Paths.get("/Users/zhanglu/Downloads/test.mp4"),StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);

        //内存映射文件
        MappedByteBuffer inmap = f1.map(MapMode.READ_ONLY, 0, f1.size());
        MappedByteBuffer outmap = f2.map(MapMode.READ_WRITE, 0, f1.size());

        //直接对缓冲区进行数据的读写操作
        byte[] dst = new byte[inmap.limit()];
        inmap.get(dst);
        outmap.put(dst);

        f2.close();
        f1.close();

        Instant t2 = Instant.now();

        Duration duration = Duration.between(t1, t2);
        System.out.println(duration.getNano());
    }

    /**
     * 通道之间的数据传输
     * @throws Exception
     */
    @Test
    public void test03() throws Exception{
        FileChannel f1 = FileChannel.open(Paths.get("/Users/zhanglu/Downloads/VLFWHPsx.mp4"), StandardOpenOption.READ);
        FileChannel f2 = FileChannel.open(Paths.get("/Users/zhanglu/Downloads/test.mp4"),StandardOpenOption.READ, StandardOpenOption.WRITE,StandardOpenOption.CREATE);

        //直接缓冲区
//        f1.transferTo(0, f1.size(), f2);

        f2.transferFrom(f1, 0, f1.size());

        f2.close();
        f1.close();
    }

    /**
     * 分散(Scatter) 与 聚集(Gather)
     * 分散读取：将通道汇总的数据分散到多个缓冲区中
     * 聚集写入：将多个缓冲区中的数据聚集到通道中
     */
    @Test
    public void test04() throws Exception {
        RandomAccessFile f1 = new RandomAccessFile("/Users/zhanglu/Desktop/lu/workspace/dev_workspace/idea/JavaBase/src/main/java/nio/channel/1.txt", "rw");

        //获取通道
        FileChannel fc1 = f1.getChannel();

        //分配指定大小的缓冲区
        ByteBuffer buf1 = ByteBuffer.allocate(100);
        ByteBuffer buf2 = ByteBuffer.allocate(1024);

        //分散读取
        ByteBuffer[] bufs = {buf1, buf2};
        fc1.read(bufs);

        for(ByteBuffer b : bufs){
            b.flip();
        }

        System.out.println(new String(bufs[0].array(), 0, bufs[0].limit()));
        System.out.println("--------------------");
        System.out.println(new String(bufs[1].array(), 0, bufs[1].limit()));

        //聚集写入
        RandomAccessFile f2 = new RandomAccessFile("/Users/zhanglu/Desktop/lu/workspace/dev_workspace/idea/JavaBase/src/main/java/nio/channel/2.txt", "rw");
        FileChannel fc2 = f2.getChannel();
        fc2.write(bufs);
    }

    /**
     * 字符集：Charset
     */
    @Test
    public void test05() throws Exception {
        //所有字符集
        Map<String, Charset> map = Charset.availableCharsets();
        Charset charset = Charset.forName("GBK");
        
        //获取编码器
        CharsetEncoder encoder = charset.newEncoder();

        //获取解码器
        CharsetDecoder decoder = charset.newDecoder();

        CharBuffer buffer = CharBuffer.allocate(1024);
        
        buffer.put("java基础学习NIO");
        buffer.flip();

        //编码
        ByteBuffer b1 = encoder.encode(buffer);

        for (int i = 0; i < buffer.limit(); i++) {
            System.out.println(b1.get());
        }

        //解码
        b1.rewind();
        CharBuffer c1 = decoder.decode(b1);
        System.out.println(c1.toString());

        b1.rewind();
        Charset charset1 = Charset.forName("utf-8");
        CharBuffer c2 = charset1.decode(b1);
        System.out.println(c2.toString());

    }

}
